/*
 * model.js, v1.0 2007-10-31, all data structure definitions used in the app.
 * author: Peter
 */

/**
 * Data structure represents colume.
 */
function Column(uri, name) {
    var self = this;
    this.type = "Column";
    this.uri = uri;
    this.name = name;
    this.parent = false;
    this.equals = function(column) {
        return (self.uri == column.uri ? true : false);
    } 
}

/**
 * Data structure represents table.
 */
function Table(uri, name, id) {
    var self = this;
    this.type = "Table";
    this.uri = uri;
    this.name = name;
    this.id = id;
    this.parent = false;
    this.columns = [];
    this.primaryKeys = [];
    this.foreignKeys = [];
    this.addColumn = function(col) {
        col.parent = self;
        self.columns.push(col);
    }
    this.equals = function(tbl) {
        return (self.uri == tbl.uri ? true : false);
    }
}

/**
 * Data structure represents data source schema.
 */
function DataSource(uri) {
    var self = this;
    this.type = "DataSource";
    this.uri = uri;
    this.tables = [];
    this.addTable = function(tb) {
        tb.parent = self;
        self.tables.push(tb);
    }
    this.equals = function(dbs) {
        return (self.uri == dbs.uri ? true : false);
    }
}

/**
 * Data structure represents property.
 */
function Property(uri, name) {
    var self = this;
    this.type = "Property";
    this.uri = uri;
    this.object = false;
    this.isValue = true;
    this.name = name;
    this.parent = false;
    this.equals = function(prop) {
        return (self.uri == prop.uri ? true : false);
    }
}

/**
 * Data structure represents ontology
 */
function Ontology(uri, name) {
    var self = this;
    this.type = "Ontology";
    this.uri = uri;
    this.name = name;
    this.properties = [];
    // to check whether a Property is this ontology's property.
    this.containsProperty = function(prop) {
        var ret = false;
        for (var i = 0; i < self.properties.length; i++) {
            if (self.properties[i].equals(prop)) {
                ret = true;
                break;
            }
        }
        return ret;
    }
    this.addProperty = function(py) {
        py.parent = self;
        self.properties.push(py);
    }
    this.equals = function(onto) {
        if (self.uri == onto.uri) {
            return true;
        } else {
            return false;
        }
    }
    // to check whether another ontology is a object of properties.
    this.isObject = function(onto) {
        for (var i = 0; i < self.properties.length; i++) {
            if (self.properties[i].object == onto.uri)
                return i + 1;
        }
        return false;
    }
    // get url of ralation property between this and another ontology.
    this.getRelationProp = function(onto) {
        for (var i = 0; i < self.properties.length; i++) {
            if (self.properties[i].object == onto.uri)
                return self.properties[i].uri;
        }
        return "";
    }
}

/**
 * Mapping Table is the bottom level unit on canvas, represents a table.
 */
function MappingTable(tb, id) {
    var self = this;
    this.table = tb;
    this.id = id;
    this.type = "MappingTable";
    this.content = OAT.Dom.create("div", {}, "mapping_table");
    self.content.belong = self;
    var titleArea = OAT.Dom.create("div", {}, "mapping_title");
    var titleText = OAT.Dom.create("div", {}, "mapping_title_text");
    titleText.innerHTML = "Table: " + self.table.name;
    titleArea.appendChild(titleText);
    this.mappingOntos = [];
    self.content.appendChild(titleArea);
    this.draw = function(x, y)  {
        // minus dbres/onto view width  
        self.content.style.left = x - screen.width / 5;
        // minus menu height
        self.content.style.top = y - style.menuHeight;
        globalVar.canvas.appendChild(self.content);
        OAT.Drag.create(self.content, self.content, {type:OAT.Drag.TYPE_XY, cursor: true, endFunction:self.updateRelations});
    }
    // remove the mapping table instance from the canvas and globalVar.mappingTables array.
    this.remove = function() {
        var num = globalVar.mappingTables.length;
        deleteArrayElement(globalVar.mappingTables, self);
        while (self.mappingOntos.length > 0) {
            self.mappingOntos[length].remove();
            self.mappingOntos.length--;
        }
        OAT.Dom.unlink(self.content);
        delete self;
    }
    // object is selected.
    this.select = function(event) {
        if (globalVar.selectElement) {
            globalVar.selectElement.deselect();
        }
        globalVar.selectElement = self;
        self.content.style.backgroundColor = style.highlightColor;
        if (event.stopPropagation)
	    event.stopPropagation();
	else
	    event.cancelBubble = true;
    }
    // object is deselected.
    this.deselect = function(event) {
        self.content.style.backgroundColor = "";
    }
    this.editUri = function() {
        var tmp = prompt("New URI: ", self.table.uri);
        if (tmp == null)
            return;
        self.table.uri = tmp;
        var columns = self.table.columns;
        for (var i = 0; i < columns.length; i++) {
            var uri = self.table.uri + "." + columns[i].name;
            columns[i].uri = uri;
        }
    }
    this.editName = function() {
        var tmp = prompt("New Name: ", self.table.name);
        if (tmp != null) {
            self.table.name = tmp;
        }
    }
    this.addMappingOnto = function (mo) {
        if (mo == null) {
            return;
        }
        mo.parent = self;
        self.content.appendChild(mo.content);
        self.mappingOntos.push(mo);
    }
    this.removeMappingOnto = function(mo) {
        deleteArrayElement(self.mappingOntos, mo);
    }
    this.equals = function(mt) {
        if (mt.type != "MappingTable")
            return false;
        return (self.id == mt.id ? true : false);
    }
    this.updateRelations = function() {
        for (var i = 0; i < self.mappingOntos.length; i++) {
           self.mappingOntos[i].updateRelations();
        }
    }
    // click to select.
    OAT.Dom.attach(self.content, "click", self.select);
}

/**
 * mapping ontology represents a displayed ontology.
 */
function MappingOnto(onto, id) {
    var self = this;
    this.type = "MappingOnto";
    this.id = id;
    this.hasIRIClass = false;
    this.iriClass = null;
    this.ontology = onto;
    this.relations = [];
    this.content = OAT.Dom.create("div", {}, "mapping_onto");
    self.content.belong = self;
    var titleArea = OAT.Dom.create("div", {}, "mapping_title");
    var titleText = OAT.Dom.create("div", {}, "mapping_tittle_text");
    titleText.innerHTML = "Ontology: " + self.ontology.name;
    titleArea.appendChild(titleText);
    self.content.appendChild(titleArea);
    this.parent = null;
    this.mappingProperties = [];
    // remove 
    this.remove = function() {
        if (self.parent != null) {
            self.parent.removeMappingOnto(self);
        }
        for (var i = 0; i < self.mappingProperties.length; i++) {
            self.mappingProperties[i].remove();
        }
        while (self.relations.length > 0) {
            self.relations[length].remove();
            self.relations.length--;
        }
        OAT.Dom.unlink(self.content);
        delete self;
    }
    // object is selected.
    this.select = function(event) {
        if (globalVar.selectElement) {
            globalVar.selectElement.deselect();
        }
        globalVar.selectElement = self;
        self.content.style.backgroundColor = style.highlightColor;
        if (event.stopPropagation)
	    event.stopPropagation();
	else
	    event.cancelBubble = true;
    }
    // object is deselected.
    this.deselect = function(event) {
        self.content.style.backgroundColor = "";
    }
    this.addRelation = function(re) {
        /*
        for (var i = 0; i < relations.length; i++) {
            if (relations[i].equals(re))
                return;
        }
        */
        self.relations.push(re);
    }
    this.removeRelation = function(re) {
        deleteArrayElement(self.relations, re);
    }
    this.updateRelations = function() {
        for (var i = 0; i < self.relations.length; i++) {
            self.relations[i].draw();
        }
    }
    // edit uri/name
    this.editUri = function() {
        var tmp = prompt("New URI: ", self.ontology.uri);
        if (tmp != null)
            self.ontology.uri = tmp;
    }
    this.editName = function() {
        var tmp = prompt("New Name: ", self.ontology.name);
        if (tmp != null)
            self.ontology.name = tmp;
    }
    this.addMappingProperty = function(mp) {
        mp.parent = self;
        self.mappingProperties.push(mp);
        self.content.appendChild(mp.content);
    }
    this.removeMappingProperty = function(mp) {
        deleteArrayElement(self.mappingProperties, mp);
    }
    this.equals = function(mo) {
        if (mo.type != "MappingOnto")
            return false;
        return (self.id == mo.id ? true : false);
    }
    OAT.Dom.attach(self.content, "click", self.select);
}

/**
 * represents a displayed property.
 */
function MappingProperty(prop, id) {
    var self = this;
    this.type = "MappingProperty";
    this.id = id;
    this.property = prop;
    this.content = OAT.Dom.create("div", {}, "mapping_property");
    self.content.belong = self;
    var icon = OAT.Dom.image("images/MacWin_maximize.gif","",15,15);
    icon.className = "property_icon";
    var bodyArea = OAT.Dom.create("div", {}, "mapping_body");
    var bodyText = OAT.Dom.create("div", {}, "mapping_body_text");
    bodyText.innerHTML = "Property: " + self.property.name;
    bodyArea.appendChild(icon);
    bodyArea.appendChild(bodyText);
    self.content.appendChild(bodyArea);
    this.parent = null;
    this.mappingColumn = null;
    this.remove = function() {
        if (self.parent != null) {
            self.parent.removeMappingProperty(self);
        }
        if (self.mappingColumn != null)
            self.mappingColumn.remove();
        OAT.Dom.unlink(self.content);
        delete self;
    }
    // object is selected.
    this.select = function(event) {
        if (globalVar.selectElement) {
            globalVar.selectElement.deselect();
        }
        globalVar.selectElement = self;
        self.content.style.backgroundColor = style.highlightColor;
        if (event.stopPropagation)
	    event.stopPropagation();
	else
	    event.cancelBubble = true;
    }
    // object is deselected.
    this.deselect = function() {
        self.content.style.backgroundColor = "";
    }
    this.editUri = function() {
        var tmp = prompt("New URI: ", self.property.uri);
        if (tmp != null)
            self.property.uri = tmp;
    }
    this.editName = function() {
        var tmp = prompt("New Name: ", self.property.name);
        if (tmp != null)
            self.property.name = tmp;
    }
    this.setMappingColumn = function(mc) {
        if (mc == null) {
            if (self.mappingColumn != null) {
                self.mappingColumn.remove();
            }
            self.mappingColumn = null;
        }
        if (self.mappingColumn != null) {
            alert("Mapping column exsits, remove it first. ");
            return;
        }
        self.mappingColumn = mc;
        self.content.appendChild(mc.content);
    }
    this.equals = function(mp) {
        if (mp.type != "MappingProperty")
            return false;
        return (self.id == mp.id ? true : false);
    }
    OAT.Dom.attach(self.content, "click", self.select);
}

/**
 * represents a displayed column.
 */
function MappingColumn(col, id) {
    var self = this;
    this.type = "MappingColumn";
    this.column = col;
    this.id = id;
    this.content = OAT.Dom.create("div", {}, "mapping_column");
    self.content.belong = self;
    var bodyArea = OAT.Dom.create("div", {}, "mapping_body");
    var bodyText = OAT.Dom.create("div", {}, "mapping_body_text");
    bodyText.innerHTML = "Column: " + self.column.name;
    bodyArea.appendChild(bodyText);
    self.content.appendChild(bodyArea);
    this.parent = null;
    this.remove = function() {
        if (self.parent) {
            self.parent.setMappingColumn(null);
        }
        OAT.Dom.clear(self.content);
        OAT.Dom.unlink(self.content);
    }
    // object is selected.
    this.select = function(event) {
        if (globalVar.selectElement) {
            globalVar.selectElement.deselect();
        }
        globalVar.selectElement = self;
        self.content.style.backgroundColor = style.highlightColor;
        if (event.stopPropagation)
	    event.stopPropagation();
	else
	    event.cancelBubble = true;
    }
    // object is deselected.
    this.deselect = function() {
        self.content.style.backgroundColor = "";
    }
    this.editUri = function() {
        var tmp = prompt("New URI: ", self.column.uri);
        if (tmp != null)
            self.column.uri = tmp;
    }
    this.editName = function() {
        var tmp = prompt("New Name: ", self.column.name);
        if (tmp != null)
            self.column.name = tmp;
    }
    this.id = function(mc) {
        if (mc.type != "MappingColumn")
            return false;
        return (self.id == mc.id ? true : false);
    }
    OAT.Dom.attach(self.content, "click", self.select);
}

/**
 * represents a relationship between two ontologies.
 */
function Relation(sub, obj, id) {
    var self = this;
    this.content = OAT.Dom.create("div", {}, "relation");
    this.subject = sub;
    this.object = obj;
    this.rid = id;
    this.propUri = "";
    this.color = "black";
    this.joinInfo = false;
    globalVar.canvas.appendChild(self.content);
    this.setColor = function(c) {
        self.color = c;
    }
    // display this relation on canvas.
    this.draw = function() {
        if (!(self.subject && self.object)) {
            return;
        }
        OAT.Dom.clear(self.content);
        var jg = new jsGraphics(self.content);
        jg.clear();
        jg.setColor(self.color);
        jg.setStroke(2);
        if (!self.subject.parent.equals(self.object.parent)) {
            var pos1 = OAT.Dom.position(self.subject.parent.content);
            var size1 = OAT.Dom.getWH(self.subject.parent.content);
            var pos2 = OAT.Dom.position(self.object.parent.content);
            var size2 = OAT.Dom.getWH(self.object.parent.content);
            var left, top, right, bottom;
            var x1, y1, x2, y2;
            if ( pos2[1] > (pos1[1] + size1[1])) {
                var cos1 = pos1[0] + Math.round(size1[0] / 2);
                var cos2 = pos2[0] + Math.round(size2[0] / 2);
                if (cos1 < cos2 ) {
                    left = cos1;
                    right = cos2;
                    x1 = 0;
                    x2 = right - left;
                } else {
                    left = cos2;
                    right = cos1;
                    x1 = right - left;
                    x2 = 0;
                }
                top = pos1[1] + size1[1];
                bottom = pos2[1];
                y1 = 0;
                y2 = bottom - top;
            } else if ( pos1[1] > (pos2[1] + size2[1])) {
                var cos1 = pos1[0] + Math.round(size1[0] / 2);
                var cos2 = pos2[0] + Math.round(size2[0] / 2);
                if (cos1 < cos2 ) {
                    left = cos1;
                    right = cos2;
                    x1 = 0;
                    x2 = right - left;
                } else {
                    left = cos2;
                    right = cos1;
                    x1 = right - left;
                    x2 = 0;
                }
                top = pos2[1] + size2[1];
                bottom = pos1[1];
                y1 = bottom - top;
                y2 = 0;
            } else {
                var cos1 = pos1[1] + Math.round(size1[1] / 2);
                var cos2 = pos2[1] + Math.round(size2[1] / 2);
                if (cos1 < cos2) {
                    top = cos1;
                    bottom = cos2;
                    y1 = 0;
                    y2 = bottom - top;
                } else {
                    top = cos2;
                    bottom = cos1;
                    y1 = bottom - top;
                    y2 = 0;
                }
                if (pos1[0] + size1[0] < pos2[0]) {
                    left = pos1[0] + size1[0];
                    right = pos2[0];
                    x1 = 0;
                    x2 = right - left;
                } else {
                    left = pos2[0] + size2[0];
                    right = pos1[0];
                    x1 = right - left;
                    x2 = 0;
                }
            }
            var width = right - left;
            left = left - Math.round(screen.width / 5) + 2;
            if (width < 10) {
                width = 10;
                left = left - 5;
            }
            self.content.style.left = left;
            self.content.style.width = width;
            var height = bottom - top;
            top = top - style.menuHeight + 1;
            if (height < 10) {
                height = 10;
                top = top - 5;
            }
            // minus menu heigth and add boder height
            self.content.style.top = top;
            self.content.style.height = height;
            jg.drawLine(x1, y1, x2, y2);
            var endX = (x2 > x1) ? x2 -5: x2;
            var endY = (y2 > y1) ? y2 - 5: y2;
            jg.fillRect(endX, endY, 5, 5);
        } else {
            var pos = OAT.Dom.position(self.subject.content);
            var size = OAT.Dom.getWH(self.subject.content);
            var x = pos[0] + size[0] - 10 - Math.round(screen.width / 5);
            var y = pos[1] - 30 - style.menuHeight;
            self.content.style.left = x;
            self.content.style.top = y;
            self.content.style.width = 40;
            self.content.style.height = 40;
            jg.drawEllipse(0, 0, 40, 40);
        }
        jg.paint();
    }
    // check whether mapping table is in this relation.
    this.contains = function(mt) {
        if (self.subject..parent.equals(mt) || self.object.parent.equals(mt)) {
            return true;
        } else {
            return false;
        }
    }
    this.equals = function(re) {
        if (re.rid == self.rid)
            return true;
        else
            return false; 
    }
    this.remove = function() {
        if (self.subject != self.object) {
            if (self.subject)
                self.subject.removeRelation(self);
            if (self.object)
                self.object.removeRelation(self);
        } else {
            self.subject.removeRelation(self);
        }
        OAT.Dom.clear(self.content);
        OAT.Dom.unlink(self.content);
    }
    // object is selected.
    this.select = function(event) {
        if (globalVar.selectElement) {
            globalVar.selectElement.deselect();
        }
        globalVar.selectElement = self;
        self.content.style.border = "1px dashed black";
        if (event.stopPropagation)
	    event.stopPropagation();
	else
	    event.cancelBubble = true;
    }
    // object is deselected.
    this.deselect = function() {
        self.content.style.border = "";
    }
    this.config = function() {
        configRelation(self);
    }
    OAT.Dom.attach(self.content, "click", self.select);
    OAT.Dom.attach(self.content, "dblclick", self.config);
}

/** stores info of relations. */
function JoinInfo() {
    var self = this;
    this.joinId = false;
    this.left = false;
    this.right = false;
    this.subId = "";
    this.objId = "";
    this.leftJoin = false;
    this.rightJoin = false;
    this.refs = [];
    this.condition = "###ref1### = ###ref2###";
    this.addRef = function(ref) {
        self.refs.push(ref);
    }
}

/** represents reference string in JoinInfo structure. */
function Ref(id, str, father) {
    this.id = id;
    this.str = str;
    this.father = father;
}

